package com.spsoft.vatoutinv.service.pricetaxseparation.impl;

import cn.hutool.core.util.ObjectUtil;
import com.spsoft.core.enums.ResponseEnum;
import com.spsoft.core.exception.BusinessException;
import com.spsoft.core.utils.BeanUtils;
import com.spsoft.vatoutinv.constants.CommonConstant;
import com.spsoft.vatoutinv.entity.vmsmanualinvoice.VmsManualBase;
import com.spsoft.vatoutinv.entity.vmsmanualinvoice.VmsManualDetail;
import com.spsoft.vatoutinv.entity.vmsmanualinvoice.VmsManualInvoice;
import com.spsoft.vatoutinv.enums.CommonEnum;
import com.spsoft.vatoutinv.model.dto.vmsmanualinvoice.VmsManualBaseDto;
import com.spsoft.vatoutinv.model.dto.vmsmanualinvoice.VmsManualDetailDto;
import com.spsoft.vatoutinv.model.dto.vmsmanualinvoice.VmsManualInvoiceDto;
import com.spsoft.vatoutinv.service.pricetaxseparation.PriceTaxSeparationService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * 价税分离实现层（手工开票使用）
 *
 * @author : chenxudong
 * @date : 2023/12/4 13:12
 */
@Slf4j
@Service("manualPriceTaxSeparationService")
public class PriceTaxSeparationServiceImpl implements PriceTaxSeparationService {

    private static final String LOG_MSG = "(手工开票价税分离实现层)";

    @Override
    public VmsManualInvoiceDto priceTaxSeparation(VmsManualInvoiceDto vmsManualInvoiceDto) throws BusinessException {
        long start = System.currentTimeMillis();

        // 校验数据合法性
        checkTaxSeparation(vmsManualInvoiceDto);

        // 价税分离
        doTaxSeparation(vmsManualInvoiceDto);

        long end = System.currentTimeMillis();
        log.info("{}价税分离完成耗时:{}ms", LOG_MSG, end - start);
        return vmsManualInvoiceDto;
    }

    @Override
    public VmsManualInvoice priceTaxSeparation(VmsManualInvoice vmsManualInvoice) throws BusinessException {
        // 参数校验
        VmsManualBase vmsManualBase = vmsManualInvoice.getVmsManualBase();
        List<VmsManualDetail> vmsManualDetailList = vmsManualInvoice.getVmsManualDetailList();
        if (ObjectUtil.isNull(vmsManualBase) || ObjectUtil.isNull(vmsManualDetailList)) {
            throw new BusinessException(ResponseEnum.TAXSEPARATION_NULL);
        }

        // VmsManualInvoice转为VmsManualInvoiceDto 进行价税分离
        VmsManualInvoiceDto invoiceDto = new VmsManualInvoiceDto();
        VmsManualBaseDto baseDto = new VmsManualBaseDto();
        BeanUtils.copyProperties(vmsManualBase, baseDto);
        List<VmsManualDetailDto> detailDtoList = BeanUtils.copyListProperties(vmsManualDetailList, VmsManualDetailDto::new);

        invoiceDto.setVmsManualBaseDto(baseDto);
        invoiceDto.setVmsManualDetailDtoList(detailDtoList);

        // 价税分离
        VmsManualInvoiceDto priceTaxInvoiceDto = priceTaxSeparation(invoiceDto);

        // VmsManualInvoiceDto转为VmsManualInvoice 返回
        VmsManualBase vmsManualBase2 = new VmsManualBase();
        VmsManualBaseDto vmsManualBaseDto = priceTaxInvoiceDto.getVmsManualBaseDto();
        List<VmsManualDetailDto> vmsManualDetailDtoList = priceTaxInvoiceDto.getVmsManualDetailDtoList();
        BeanUtils.copyProperties(vmsManualBaseDto, vmsManualBase2);
        List<VmsManualDetail> vmsManualDetailList2 = BeanUtils.copyListProperties(vmsManualDetailDtoList, VmsManualDetail::new);

        vmsManualInvoice.setVmsManualBase(vmsManualBase2);
        vmsManualInvoice.setVmsManualDetailList(vmsManualDetailList2);
        return vmsManualInvoice;
    }

    /**
     * 校验数据合法性
     */
    private void checkTaxSeparation(VmsManualInvoiceDto vmsManualInvoiceDto) {
        // 校验外层数据是否为空
        if (ObjectUtil.isNull(vmsManualInvoiceDto)) {
            printException(ResponseEnum.TAXSEPARATION_NULL);
        }

        // 校验发票主数据是否为空
        if (ObjectUtil.isNull(vmsManualInvoiceDto.getVmsManualBaseDto())) {
            printException(ResponseEnum.TAXSEPARATION_NULL2);
        }

        // 校验发票主数据价税合计是否为空
        if (ObjectUtil.isNull(vmsManualInvoiceDto.getVmsManualBaseDto().getJshj())) {
            printException(ResponseEnum.TAXSEPARATION_NULL3);
        }

        // 校验发票主数据不含税金额是否为空
        if (ObjectUtil.isNull(vmsManualInvoiceDto.getVmsManualBaseDto().getHjje())) {
            printException(ResponseEnum.TAXSEPARATION_NULL4);
        }

        /**
         * 校验明细行数据
         */
        // 校验明细数据是否为空
        String jems = vmsManualInvoiceDto.getVmsManualBaseDto().getJems();
        List<VmsManualDetailDto> detailList = vmsManualInvoiceDto.getVmsManualDetailDtoList();
        if (ObjectUtil.isEmpty(detailList)) {
            printException(ResponseEnum.TAXSEPARATION_NULL5);
        }

        StringBuilder msg = new StringBuilder();
        for (int i = 0; i < detailList.size(); i++) {
            String errorMsg = "第" + (i + 1) + "行：";
            try {
                checkSeparationDetailList(jems, errorMsg, detailList.get(i));
            } catch (BusinessException exception) {
                msg.append(exception.getMsg()).append(CommonConstant.STRING_CHARSET);
            }

        }
        if (StringUtils.isNotEmpty(msg)) {
            String returnMsg = msg.toString();
            if (returnMsg.endsWith(CommonConstant.STRING_CHARSET)) {
                returnMsg = returnMsg.substring(0, returnMsg.lastIndexOf(CommonConstant.STRING_CHARSET));
            }
            printException(ResponseEnum.BUSINESS_ERROR.getCode(), returnMsg);
        }
    }

    /**
     * 校验单个明细行数据
     */
    private void checkSeparationDetailList(String jems, String errorMsg, VmsManualDetailDto vmsManualDetailDto) throws BusinessException {
        // 校验发票的金额模式只能为01或02
        if (!CommonConstant.STR_01.equals(jems) && !CommonConstant.STR_02.equals(jems)) {
            printException(ResponseEnum.TAXSEPARATION_ITEM_JEMS_ERROR.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_JEMS_ERROR.getMsg());
        }
        // 校验明细数据是否为空
        if (ObjectUtil.isNull(vmsManualDetailDto)) {
            printException(ResponseEnum.TAXSEPARATION_NULL6.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_NULL6.getMsg());
        }
        // 校验明细金额不能为空
        if (ObjectUtil.isNull(vmsManualDetailDto.getJe())) {
            printException(ResponseEnum.TAXSEPARATION_ITEM_JE_NULL.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_JE_NULL.getMsg());
        }
        // 校验明细税率不能为空
        if (ObjectUtil.isNull(vmsManualDetailDto.getSlv())) {
            printException(ResponseEnum.TAXSEPARATION_ITEM_SL_NULL.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_SL_NULL.getMsg());
        }
        // 校验明细数量和单价需要同时为空或同时不为空
        if (StringUtils.isBlank(vmsManualDetailDto.getSl())) {
            if (StringUtils.isNotBlank(vmsManualDetailDto.getDj())) {
                printException(ResponseEnum.TAXSEPARATION_ITEM_SL_DJ_ERROR.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_SL_DJ_ERROR.getMsg());
            }
        }
        if (StringUtils.isBlank(vmsManualDetailDto.getDj())) {
            if (StringUtils.isNotBlank(vmsManualDetailDto.getSl())) {
                printException(ResponseEnum.TAXSEPARATION_ITEM_SL_DJ_ERROR.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_SL_DJ_ERROR.getMsg());
            }
        }
        // 校验明细金额不能为0
        if (ObjectUtil.isNotNull(vmsManualDetailDto.getJe()) && BigDecimal.ZERO.compareTo(vmsManualDetailDto.getJe()) == 0) {
            printException(ResponseEnum.TAXSEPARATION_ITEM_JE_ERROR.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_JE_ERROR.getMsg());
        }
        // 校验明细单价不能为0
        if (StringUtils.isNotBlank(vmsManualDetailDto.getDj()) && BigDecimal.ZERO.compareTo(new BigDecimal(vmsManualDetailDto.getDj())) == 0) {
            printException(ResponseEnum.TAXSEPARATION_ITEM_DJ_ERROR.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_DJ_ERROR.getMsg());
        }
        // 校验明细数量不能为0
        if (StringUtils.isNotBlank(vmsManualDetailDto.getSl()) && BigDecimal.ZERO.compareTo(new BigDecimal(vmsManualDetailDto.getSl())) == 0) {
            printException(ResponseEnum.TAXSEPARATION_ITEM_SL_ERROR.getCode(), errorMsg + ResponseEnum.TAXSEPARATION_ITEM_SL_ERROR.getMsg());
        }
    }

    /**
     * 价税分离
     */
    private void doTaxSeparation(VmsManualInvoiceDto vmsManualInvoiceDto) throws BusinessException {
        // 金额、税额初始化
        BigDecimal hjje = BigDecimal.ZERO;
        BigDecimal hjse = BigDecimal.ZERO;
        BigDecimal hjwc = BigDecimal.ZERO;

        for (VmsManualDetailDto detailDto : vmsManualInvoiceDto.getVmsManualDetailDtoList()) {
            VmsManualBaseDto baseDto = vmsManualInvoiceDto.getVmsManualBaseDto();
            // 单行明细价税分离
            separateItem(baseDto, detailDto);
            // 尾差记录
            BigDecimal wcje = detailDto.getJe().multiply(detailDto.getSlv()).subtract(detailDto.getSe()).setScale(2, RoundingMode.HALF_UP);

            if (ObjectUtil.isEmpty(detailDto.getKce())) {
                hjwc = hjwc.add(wcje);
            }
            hjje = hjje.add(detailDto.getJe());
            hjse = hjse.add(detailDto.getSe());
        }

        /**
         * 累计税额误差计算 当累计税额误差大于1.27时 抛出异常
         * 扣除额的发票只有单条明细 不做税额误差累计的计算
         */
        if (hjwc.compareTo(new BigDecimal(CommonConstant.STR_127)) > 0) {
            throw new BusinessException(ResponseEnum.TAXSEPARATION_SE_WC_TOTAL);
        }
        // 重置金额模式为不含税模式
        vmsManualInvoiceDto.getVmsManualBaseDto().setJems(CommonEnum.AMOUNT_MODE_01.getKey());
        // 重新计算合计金额 合计税额 价税合计
        BigDecimal jshj = hjje.add(hjse).setScale(2, RoundingMode.HALF_UP);
        vmsManualInvoiceDto.getVmsManualBaseDto().setHjje(hjje);
        vmsManualInvoiceDto.getVmsManualBaseDto().setHjse(hjse);
        vmsManualInvoiceDto.getVmsManualBaseDto().setJshj(jshj);
    }

    /**
     * 发票明细价税分离
     */
    private void separateItem(VmsManualBaseDto baseDto, VmsManualDetailDto detailDto) throws BusinessException {

        BigDecimal kce = detailDto.getKce();
        // 如果差额征税类型为全额征税(01),kce赋值为空,不参与计算
        if (CommonEnum.CEZS_01.getKey().equals(baseDto.getCezslxdm())) {
            // 全额开票，扣除额置为0
            detailDto.setKce(BigDecimal.ZERO);
            kce = null;
        }

        BigDecimal bhsje;
        BigDecimal se;

        if (CommonEnum.AMOUNT_MODE_02.getKey().equals(baseDto.getJems())) {
            /**
             * 含税价税分离步骤
             * 差额征税只能是不含税模式
             *
             * 1.税额 = （含税金额/（1 + 税率）） * 税率
             * 2.不含税金额 = 含税金额 - 税额
             * 3.不含税单价 = 不含税金额/数量
             */
            // 税额
            BigDecimal bigDecimalOne = new BigDecimal(CommonConstant.STR_1);
            if (StringUtils.isNotBlank(detailDto.getSl()) && BigDecimal.ZERO.compareTo(new BigDecimal(detailDto.getSl())) != 0
                    && StringUtils.isNotBlank(detailDto.getDj()) && BigDecimal.ZERO.compareTo(new BigDecimal(detailDto.getDj())) != 0) {
                // 重新计算价税合计（数量 * 含税单价）
                BigDecimal jshj = new BigDecimal(detailDto.getSl()).multiply(new BigDecimal(detailDto.getDj()));
                se = jshj.divide(bigDecimalOne.add(detailDto.getSlv()), 25, RoundingMode.HALF_UP)
                        .multiply(detailDto.getSlv()).setScale(2, RoundingMode.HALF_UP);
            } else {
                se = detailDto.getJe().divide(bigDecimalOne.add(detailDto.getSlv()), 25, RoundingMode.HALF_UP)
                        .multiply(detailDto.getSlv()).setScale(2, RoundingMode.HALF_UP);
            }

            // 不含税金额
            bhsje = detailDto.getJe().subtract(se).setScale(2, RoundingMode.HALF_UP);
            // 单价不等于空,赋值单价
            if (StringUtils.isNotBlank(detailDto.getDj()) && BigDecimal.ZERO.compareTo(new BigDecimal(detailDto.getDj())) != 0
                    && StringUtils.isNotBlank(detailDto.getSl()) && BigDecimal.ZERO.compareTo(new BigDecimal(detailDto.getSl())) != 0) {
                BigDecimal sl = new BigDecimal(detailDto.getSl()).stripTrailingZeros();
                detailDto.setSl(sl.toPlainString());
                BigDecimal xmdj = bhsje.divide(sl, 25, RoundingMode.HALF_UP);
                detailDto.setDj(formatBigDecimalToString(xmdj));
            }
            detailDto.setJe(bhsje);
            detailDto.setSe(se);

        } else {
            /**
             * 不含税价税分离处理
             * 用户传递不含税金额和税额，对用户传递数据进行计算校验，
             * 用户传递税额如果和计算后数据不一致，日志提示。
             */
            // 带扣除额的税额计算
            if (ObjectUtil.isNotNull(kce) && ObjectUtil.isNotEmpty(kce) && kce.compareTo(BigDecimal.ZERO) != 0) {
                BigDecimal bigDecimalZero = BigDecimal.ZERO;
                if (detailDto.getJe().compareTo(bigDecimalZero) < 0 && kce.compareTo(bigDecimalZero) > 0) {
                    kce = kce.negate();
                }
                /**
                 * 1.不含税差额 = （含税金额-扣除额）/（1 + 税率）; 不含税金额  = 不含税差额 + 扣除额
                 * 2.税额 = 含税金额 - 不含税金额
                 */
                // se = detailDto.getJe().subtract(kce).setScale(2, RoundingMode.HALF_UP).multiply(detailDto.getSlv());
                // 20240409：不含税模式下税额以前端传递为准，如果计算可能会出现差一分钱的问题
                // （不含税差额不保留两位小数，直接乘以税率，计算出的税额，就不会出现一分钱误差了）
                se = detailDto.getSe();
                // 如果项目金额大于0,计算的税额也要大于0,如果小于0,默认为0.00
                if (detailDto.getJe().compareTo(bigDecimalZero) > 0 && se.compareTo(bigDecimalZero) < 0) {
                    se = new BigDecimal(CommonConstant.STR_000);
                }
                // 如果项目金额小于0,计算的税额也要小于0,如果大于0,默认为0.00
                if (detailDto.getJe().compareTo(bigDecimalZero) < 0 && se.compareTo(BigDecimal.ZERO) > 0) {
                    se = new BigDecimal(CommonConstant.STR_000);
                }
            } else {
                // 格式化明细的数量和单价
                if (StringUtils.isNotBlank(detailDto.getSl()) && BigDecimal.ZERO.compareTo(new BigDecimal(detailDto.getSl())) != 0
                        && StringUtils.isNotBlank(detailDto.getDj()) && BigDecimal.ZERO.compareTo(new BigDecimal(detailDto.getDj())) != 0) {
                    BigDecimal sl = new BigDecimal(detailDto.getSl()).stripTrailingZeros();
                    detailDto.setSl(sl.toPlainString());
                    BigDecimal dj = new BigDecimal(detailDto.getDj()).stripTrailingZeros();
                    detailDto.setDj(dj.toPlainString());

                    // 有数量和单价，重新计算金额后在计算税额
                    se = sl.multiply(dj).multiply(detailDto.getSlv());
                } else {
                    // 税额金额
                    se = detailDto.getJe().multiply(detailDto.getSlv());
                }
            }
            // 税额
            detailDto.setSe(se.setScale(2, RoundingMode.HALF_UP));
        }
    }

    /**
     * 将BigDecimal转为String
     * String的最大长度为25
     */
    private String formatBigDecimalToString(BigDecimal bigDecimal) {
        if (ObjectUtil.isEmpty(bigDecimal)) {
            return StringUtils.EMPTY;
        }
        // 单价、数量先保留6位小数
        String formatResult = bigDecimal.setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
        if (formatResult.length() > 25) {
            formatResult = formatResult.substring(0, 25);
        }
        return formatResult;
    }

    /**
     * 校验发票明细中 `项目金额`与'扣除额'大小关系是否合法
     */
    private boolean checkJeAndKce(BigDecimal je, BigDecimal kce) {
        boolean isValid = true;
        BigDecimal bigDecimalZero = BigDecimal.ZERO;
        if (je.compareTo(bigDecimalZero) < 0 && kce.compareTo(bigDecimalZero) > 0) {
            kce = kce.negate();
        }
        BigDecimal ce = je.subtract(kce).setScale(2, RoundingMode.HALF_UP);
        // 校验,如果金额大于0,金额和扣除额相减应为正数,如果小于0,异常
        if (je.compareTo(bigDecimalZero) > 0 && ce.compareTo(bigDecimalZero) < 0) {
            log.error("{}差额校验失败，用户传递金额数据:{}和差额数据:{}有问题,抛出异常", LOG_MSG, je.toPlainString(), kce.toPlainString());
            isValid = false;
        }
        return isValid;
    }

    /**
     * 打印异常日志
     */
    private void printException(ResponseEnum responseEnum) throws BusinessException {
        log.error("{}价税分离异常,异常信息为:{}", LOG_MSG, responseEnum.getMsg());
        throw new BusinessException(responseEnum);
    }

    /**
     * 打印异常日志
     */
    private static void printException(String errorCode, String errorMsg) throws BusinessException {
        log.error("{}价税分离异常,异常code为:{},异常信息为:{}", LOG_MSG, errorCode, errorMsg);
        throw new BusinessException(errorCode, errorMsg);
    }
}
